#pragma once
#include <iostream>
#include <cstdio>
#include <string>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>

// systeam v共享内存
// 进程间通讯的本质上看到同一块的内存资源
// systeam 开辟共享内存的过程如下 开辟内存块->开辟虚拟内存空间(在共享区)->通过页表映射
// 所以os一定会存在大量的不同状态的共享内存->所以需要对这些进行管理—>怎么管理？先描述再组织
// pcd也一定会保存共享内存的相关属性

std::string gpathname = ".";
int gproj_id = 0x66;
int gdefaultsize = 4096; // 共享内存的大小，4kb的整数倍，

struct data
{
    int num;
    char buffer[4092];
};
class SharedMemory
{
private:
    // int shmget(key_t key,size_t size,int shmfly) 第一个参数为键值 第二个为要开辟的大小 第三个为标志位->常见的为两个
    // 一个为IPC_CREAT 存在共享内存->获取 不存在->创建 另一个为IPC_EXCL 和IPC_CREAT一起使用不能单独使用,出错会返回,同时也可以传权限
    // 对于返回值来说 成功返回一个合法的标识符 失败-1,并设置错误码
    // 为什么要传入键值 ->确保共享内存的唯一性 怎么创建键值->ftok 为什么要用户传入->相当于约定好了一块空间的名字,双方都知道
    bool CreateHelper(int flags)
    {
        _key = (gpathname.c_str(), gproj_id);
        if (_key < 0)
        {
            return false;
        }
        printf("形成键值成功:0x%x\n", _key);
        _shmid = shmget(_key, _size, flags); // 这里的返回值类似于文件描述符
        if (_shmid < 0)
        {
            perror("shmget");
            return false;
        }
        printf("shmid: %d\n", _shmid);
        return true;
    }

public:
    SharedMemory(int size = gdefaultsize)
        : _key(0), _shmid(-1), _size(size)
    {
    }
    bool Creat()
    {
        return CreateHelper(IPC_CREAT | IPC_EXCL | 0666);
    }
    bool Get()
    {
        return CreateHelper(IPC_CREAT);
    }
    bool Attach()
    {
        // void* shmat(int shmid,const void* shmaddr,int shmflg)
        // 第二个参数为虚拟空间的地址 ,第三个为选项使用缺省的就可以,因为挂载共享内存是需要设置权限的
        // 返回值成功返回虚拟地址的空间,失败返回-1
        _start_addr = shmat(_shmid, nullptr, 0);
        if ((long long)_start_addr == -1)
        {
            perror("shmat");
            return false;
        }
        std::cout << "将指定的共享内存挂接到自己进程的地址空间" << std::endl;
        printf("_start_addr : %p\n", _start_addr);
        _num = (int *)_start_addr;                      // 在共享内存的位置设置一个计数器
        _datastart = (char *)_start_addr + sizeof(int); // 将开头的位置向后移动四个字节的位置
        return true;
    }
    bool Detach()
    {
        int n = shmdt(_start_addr);
        if (n < 0)
        {
            perror("shmdt");
            return false;
        }
        std::cout << "将指定的共享内存从进程的地址空间移除" << std::endl;
        return true;
    }
    void SetZero()
    {
        *_num = 0;
    }
    void Addchar(char ch)
    {
        if (*_num == _size)
            return;
        ((char *)(_datastart))[_windex++] = ch; // 在一个数组里增加
        ((char *)(_datastart))[_windex] = 0;
        _windex %= _size;
        (*_num)++;
    }
    void Popchar(char *ch)
    {
        if (*_num == 0)
            return;
        *ch =((char *)(_datastart))[_rindex++]; // 在一个数组里面读记录读到第几个了 []相当于解引用的作用
        (*_num)--;
    }

private:
    key_t _key;
    int _size;
    int _shmid;
    void *_start_addr;
    int *_num;
    char *_datastart;
    int _windex;
    int _rindex;
};